#pragma once

#include <vector>
#include <iostream>

#include "kconfig.h"
#include "ktoken.h"

enum CommandType
{
	EMPTY_LINE,

	LABEL_LINE,

	IO_IN_INT,
	IO_IN_REAL,
	IO_IN_STRING,
	IO_IN_VAR,
	IO_OUT,

	VAR_DECL,
	VAR_DECL_EX,
	VAR_DEL,
	VAR_SET,

	STACK_PUSH,
	STACK_POP,
	STACK_POP_VAR,
	STACK_PEEK,
	STACK_SWAP,
	STACK_CLEAR,
	STACK_CLEAR_C,

	TAPE_READ_INT,
	TAPE_READ_REAL,
	TAPE_READ_STRING,
	TAPE_READ_VAR,
	TAPE_WRITE,
	TAPE_NEXT,
	TAPE_PREV,
	TAPE_MOVE,

	CTRL_GOTO,
	CTRL_GOTO_IF,
	CTRL_CALL,
	CTRL_CALL_IF,
	CTRL_RETURN,
	CTRL_END
};

struct Command
{
	CommandType type;

	k_string identifier;
	std::vector<Token> tokenList;

	k_uint line;

	Command() : type(EMPTY_LINE), tokenList() {}

	Command(CommandType t, k_uint ln) : type(t), line(ln), tokenList() {}

	~Command()
	{
		this->tokenList.clear();
	}

#if _DEBUG
	typedef std::vector<Token>::const_iterator tokit_t;
	friend std::ostream& operator<<(std::ostream & os, const Command& cmd)
	{
		using namespace std;
		switch (cmd.type)
		{
		case EMPTY_LINE:
			os << endl;
			break;
		case LABEL_LINE:
			os << cmd.identifier << ":\n";
			break;
		case IO_IN_INT:
			os << "io.in int" << endl;
			break;
		case IO_IN_REAL:
			os << "io.in real" << endl;
			break;
		case IO_IN_STRING:
			os << "io.in string" << endl;
			break;
		case IO_IN_VAR:
			os << "io.in " << cmd.identifier << endl;
			break;
		case IO_OUT:
			os << "io.out ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case VAR_DECL:
			os << "var.decl " << cmd.tokenList[0] << " " << cmd.identifier << endl;
			break;
		case VAR_DECL_EX:
			os << "var.decl " << cmd.tokenList[0] << " " << cmd.identifier << " = ";
			for (tokit_t it = cmd.tokenList.begin()+1; it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case VAR_DEL:
			os << "var.del " << cmd.identifier << endl;
			break;
		case VAR_SET:
			os << "var.set " << cmd.identifier << " = ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case STACK_PUSH:
			os << "stack.push ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case STACK_POP:
			os << "stack.pop" << endl;
			break;
		case STACK_POP_VAR:
			os << "stack.pop " << cmd.identifier << endl;
			break;
		case STACK_PEEK:
			os << "stack.peek " << cmd.identifier << endl;
			break;
		case STACK_SWAP:
			os << "stack.swap" << endl;
			break;
		case STACK_CLEAR:
			os << "stack.clear" << endl;
			break;
		case STACK_CLEAR_C:
			os << "stack.clear " << cmd.tokenList[0] << endl;
			break;
		case TAPE_READ_INT:
			if (cmd.tokenList[0].v_int != -1)
				os << "tape.read int at " << cmd.tokenList[0].v_int << endl;
			else
				os << "tape.read int" << endl;
			break;
		case TAPE_READ_REAL:
			if (cmd.tokenList[0].v_int != -1)
				os << "tape.read real at " << cmd.tokenList[0].v_int << endl;
			else
				os << "tape.read real" << endl;
			break;
		case TAPE_READ_STRING:
			if (cmd.tokenList[0].v_int != -1)
				os << "tape.read string at " << cmd.tokenList[0].v_int << endl;
			else
				os << "tape.read string" << endl;
			break;
		case TAPE_READ_VAR:
			if (cmd.tokenList[0].v_int != -1)
				os << "tape.read " << cmd.identifier << " at " << cmd.tokenList[0].v_int << endl;
			else
				os << "tape.read " << cmd.identifier << endl;
			break;
		case TAPE_WRITE:
			os << "tape.write ";
			for (tokit_t it = cmd.tokenList.begin() + 1; it->type != TOK_EOS; ++it)
				os << *it << " ";
			if (cmd.tokenList[0].v_int != -1)
				os << "at " << cmd.tokenList[0].v_int;
			os << endl;
			break;
		case TAPE_NEXT:
			os << "tape.next" << endl;
			break;
		case TAPE_PREV:
			os << "tape.prev" << endl;
			break;
		case TAPE_MOVE:
			os << "tape.move ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case CTRL_GOTO:
			os << "ctrl.goto " << cmd.identifier << endl;
			break;
		case CTRL_GOTO_IF:
			os << "ctrl.goto " << cmd.identifier << " if ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case CTRL_CALL:
			os << "ctrl.call " << cmd.identifier << endl;
			break;
		case CTRL_CALL_IF:
			os << "ctrl.call " << cmd.identifier << " if ";
			for (tokit_t it = cmd.tokenList.begin(); it->type != TOK_EOS; ++it)
				os << *it << " ";
			os << endl;
			break;
		case CTRL_RETURN:
			os << "ctrl.return" << endl;
			break;
		case CTRL_END:
			os << "ctrl.end" << endl;
			break;
		}

		return os;
	}
#endif
};
